package com.wzw.springboot.principle.use.security.service;

import com.wzw.springboot.principle.exception.BusinessException;
import com.wzw.springboot.principle.use.security.enums.AuthenticationEnum;
import com.wzw.springboot.principle.use.security.infrastructure.model.SecurityUser;
import com.wzw.springboot.principle.use.security.infrastructure.provider.QrCodeAuthenticationToken;
import com.wzw.springboot.principle.use.security.infrastructure.provider.SmsCodeAuthenticationToken;
import com.wzw.springboot.principle.use.security.infrastructure.provider.ThirdPartyAuthenticationToken;
import com.wzw.springboot.principle.use.security.manage.TokenManager;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * @author Wangzhiwen
 */
@Service
@RequiredArgsConstructor
public class UserAuthServiceImpl implements UserAuthService {

    private final AuthenticationManager authenticationManager;

    private final TokenManager tokenManager;

    @Override
    public String login(AuthenticationEnum authenticationEnum, String principal, String credentials) {
        try {
            // setAuthenticated(false); 未认证 权限列表为空 认证成功后会把当前UserDetails对象 即认证中的 Principal 对象存到缓存中。
            // 下次用户访问没有被放行的接口时，会从缓存中拉取。这个时候需要为 认证对象 Authentication 初始化 Principal Credentials GrantedAuthority。
            // 这样后续的权限注解才会生效
            SecurityUser securityUser = switchSecurityUser(authenticationEnum, principal, credentials);
            return tokenManager.create(securityUser);
        } catch (Exception e) {
            String exception = handleAuthenticationException(e, principal);
            // 自定义异常会被 自定义的全局异常处理器捕获 不会继续执行 security FilterSecurityInterceptor 过滤器
            // 如果异常没有被捕获 /error 地址就会继续往后执行过滤器的
            throw new BusinessException(exception);
        }
    }

    protected SecurityUser switchSecurityUser(AuthenticationEnum authenticationEnum, String principal, String credentials) {
        switch (authenticationEnum) {
            case USERNAME:
                return loginByUsernamePassword(principal, credentials);
            case MOBILE:
                return loginBySmsCode(principal, credentials);
            case QR:
                return loginByQrCode(principal, credentials);
            default:
                throw new BusinessException("不支持的登录方式");
        }
    }

    public SecurityUser loginByUsernamePassword(String account, String password) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(account, password);
        return (SecurityUser) authenticationManager.authenticate(authentication).getPrincipal();

    }

    protected SecurityUser loginBySmsCode(String mobile, String code) {
        Authentication authentication = new SmsCodeAuthenticationToken(mobile, code);
        return (SecurityUser) authenticationManager.authenticate(authentication).getPrincipal();
    }

    protected SecurityUser loginByQrCode(String uuid, String code) {
        Authentication authentication = new QrCodeAuthenticationToken(uuid, code);
        return (SecurityUser) authenticationManager.authenticate(authentication).getPrincipal();
    }

    public String loginByThirdParty(String type, String authCode, String terminal) {
        ThirdPartyAuthenticationToken authentication = new ThirdPartyAuthenticationToken(type, authCode);
        authentication.setTerminal(terminal);
        SecurityUser securityUser = (SecurityUser) authenticationManager.authenticate(authentication).getPrincipal();
        return tokenManager.create(securityUser);
    }

    @Override
    public void logout(Long userId) {
        tokenManager.clean(userId);
    }

    private String handleAuthenticationException(Exception e, String account) {
        if (e instanceof UsernameNotFoundException) {
            return "账号不存在";
        }
        if (e instanceof BadCredentialsException) {
            tokenManager.authRecord(account);
            return "账号密码错误";
        }
        if (e instanceof LockedException) {
            return "账号已被锁定";
        }
        if (e instanceof DisabledException) {
            return "账号已被禁用";
        }
        if (e instanceof AccountExpiredException) {
            return "账号已过期";
        }
        if (e instanceof CredentialsExpiredException) {
            return "访问凭证已过期";
        }
        throw new BusinessException(e.getMessage());
    }
}
